package com.lyp.jackson.server.controller;

import com.lyp.jackson.api.common.annotation.DataSource;
import com.lyp.jackson.api.common.enums.DataSourceType;
import com.lyp.jackson.api.common.enums.SeckillStatEnum;
import com.lyp.jackson.model.entity.Seckill;
import com.lyp.jackson.model.mapper.SuccessKilledMapper;
import com.lyp.jackson.server.response.Result;
import com.lyp.jackson.server.service.SeckillService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 秒杀：
 *      适合单机用，集群和分布式会出现问题。
 */
@RestController
@RequestMapping("/seckill")
@Slf4j
public class SeckillController {

    private static int corePoolSize = Runtime.getRuntime().availableProcessors();
    //创建线程池  调整队列数 拒绝服务
    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, corePoolSize + 1, 10l, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(1000));


    @Autowired
    private SeckillService seckillService;

    @Autowired
    private SuccessKilledMapper successKilledMapper;


    @RequestMapping("getSeckll")
    @DataSource(DataSourceType.SLAVE1)
    public Seckill selectBySeckillId(long seckillId) {
        Seckill result = seckillService.selectBySeckillId(seckillId);
        return result;
    }


    /**
     * 普通秒杀，最low实现
     * @param seckillId
     * @return
     */
    @PostMapping("/start")
    public Result start(long seckillId) {
        Long seckillCount=0L;
        int skillNum = 1000;
        //TODO:N个购买者
        final CountDownLatch latch = new CountDownLatch(skillNum);
        //TODO:恢复库存和情况秒杀明细表
        seckillService.deleteSeckill(seckillId);
        log.info("开始秒杀一（会出现超卖）");
        for (int i = 0; i < skillNum; i++) {
            final long userId = i;
            Runnable task = () -> {
                Result result = seckillService.startSeckil(seckillId, userId);
                if (result != null) {
                    log.info("秒杀成功，用户：{}{}", userId, result.get("msg"));
                } else {
                    log.info("秒杀失败，用户：{}{}", userId, SeckillStatEnum.MUCH.getInfo());
                }
                latch.countDown();
            };
            executor.execute(task);
        }
        try {
            latch.await();//等待所有人的任务结束
            seckillCount = successKilledMapper.getSeckillCount(seckillId);
            log.info("一共秒杀出{}件商品", seckillCount);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return Result.ok(seckillCount);
    }



    /**
     * 普通秒杀(限流处理),限制商品购买总数量
     * @param seckillId
     * @return
     */
    @PostMapping("/startLimit")
    public Result startLimit(long seckillId) {
        Long seckillCount=0L;
        int skillNum = 10000;
        //TODO:N个购买者
        final CountDownLatch latch = new CountDownLatch(skillNum);
        //TODO:恢复库存和情况秒杀明细表
        seckillService.deleteSeckill(seckillId);
        log.info("限流开始秒杀一正常");
        for (int i = 0; i < skillNum; i++) {
            final long userId = i;
            Runnable task = () -> {
                Result result = seckillService.startSeckilLimit(seckillId, userId);
                if (result != null) {
                    log.info("秒杀成功，用户：{}{}", userId, result.get("msg"));
                } else {
                    log.info("秒杀失败，用户：{}{}", userId, SeckillStatEnum.MUCH.getInfo());
                }
                latch.countDown();
            };
            executor.execute(task);
        }
        try {
            latch.await();//等待所有人的任务结束
            seckillCount = successKilledMapper.getSeckillCount(seckillId);
            log.info("限流后一共秒杀出{}件商品", seckillCount);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return Result.ok(seckillCount);
    }



    /**
     * Lok锁实现秒杀
     * @param seckillId
     * @return
     */
    @PostMapping("/startLock")
    public Result startLock(long seckillId) {
        Long seckillCount=0L;
        int skillNum = 1000;
        //TODO:N个购买者
        final CountDownLatch latch = new CountDownLatch(skillNum);
        //TODO:恢复库存和情况秒杀明细表
        seckillService.deleteSeckill(seckillId);
        final long killId =  seckillId;
        log.info("Lok锁开始秒杀一可能会出现超卖101的现象");
        for (int i = 0; i < skillNum; i++) {
            final long userId = i;
//            Runnable task = () -> {
//                Result result = seckillService.startSeckilLock(killId, userId);
//                if (result != null) {
//                    log.info("秒杀成功，用户：{}{}", userId, result.get("msg"));
//                } else {
//                    log.info("秒杀失败，用户：{}{}", userId, SeckillStatEnum.MUCH.getInfo());
//                }
//                latch.countDown();
//            };
            Runnable task =new Runnable(){
                @Override
                public void run() {
                    Result result = seckillService.startSeckilLock(killId, userId);
                    if (result != null) {
                        log.info("秒杀成功，用户：{}{}", userId, result.get("msg"));
                    } else {
                        log.info("秒杀失败，用户：{}{}", userId, SeckillStatEnum.MUCH.getInfo());
                    }
                    latch.countDown();
                }
            };
            executor.execute(task);
        }
        try {
            latch.await();//等待所有人的任务结束
            seckillCount = successKilledMapper.getSeckillCount(seckillId);
            log.info("Lok锁一共秒杀出{}件商品", seckillCount);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return Result.ok(seckillCount);
    }




    /**
     * Lok锁实现秒杀
     * @param seckillId
     * @return
     */
    @PostMapping("/startAopLock")
    public Result startAopLock(long seckillId) {
        Long seckillCount=0L;
        int skillNum = 10000;
        //TODO:N个购买者
        final CountDownLatch latch = new CountDownLatch(skillNum);
        //TODO:恢复库存和情况秒杀明细表
        seckillService.deleteSeckill(seckillId);
        log.info("AOP+Lok锁开始秒杀一正常");
        for (int i = 0; i < skillNum; i++) {
            final long userId = i;
            Runnable task = () -> {
                Result result = seckillService.startSeckilAopLock(seckillId, userId);
                if (result != null) {
                    log.info("秒杀成功，用户：{}{}", userId, result.get("msg"));
                } else {
                    log.info("秒杀失败，用户：{}{}", userId, SeckillStatEnum.MUCH.getInfo());
                }
                latch.countDown();
            };
            executor.execute(task);
        }
        try {
            latch.await();//等待所有人的任务结束
            seckillCount = successKilledMapper.getSeckillCount(seckillId);
            log.info("AOP+Lok锁一共秒杀出{}件商品", seckillCount);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return Result.ok(seckillCount);
    }



    /**
     * 悲观锁秒杀一 FOR UPDATE
     * @param seckillId
     * @return
     */
    @PostMapping("/startDBPCC_ONE")
    public Result startDBPCC_ONE(long seckillId) {
        Long seckillCount=0L;
        int skillNum = 10000;
        //TODO:N个购买者
        final CountDownLatch latch = new CountDownLatch(skillNum);
        //TODO:恢复库存和情况秒杀明细表
        seckillService.deleteSeckill(seckillId);
        final long killId =  seckillId;
        log.info("悲观锁FOR UPDATE开始秒杀一正常");
        for (int i = 0; i < skillNum; i++) {
            final long userId = i;
            Runnable task = () -> {
                Result result = seckillService.startSeckilDBPCC_ONE(killId, userId);
                if (result != null) {
                    log.info("秒杀成功，用户：{}{}", userId, result.get("msg"));
                } else {
                    log.info("秒杀失败，用户：{}{}", userId, SeckillStatEnum.MUCH.getInfo());
                }
                latch.countDown();
            };
            executor.execute(task);
        }
        try {
            latch.await();//等待所有人的任务结束
            seckillCount = successKilledMapper.getSeckillCount(seckillId);
            log.info("悲观锁FOR UPDATE一共秒杀出{}件商品", seckillCount);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return Result.ok(seckillCount);
    }



    /**
     * 悲观锁秒杀 UPDATE锁表
     * @param seckillId
     * @return
     */
    @PostMapping("/startDPCC_TWO")
    public Result startDPCC_TWO(long seckillId) {
        Long seckillCount=0L;
        int skillNum = 20000;
        //TODO:N个购买者
        final CountDownLatch latch = new CountDownLatch(skillNum);
        //TODO:恢复库存和情况秒杀明细表
        seckillService.deleteSeckill(seckillId);
        final long killId =  seckillId;
        log.info("悲观锁UPDATE锁表开始秒杀一正常");
        for (int i = 0; i < skillNum; i++) {
            final long userId = i;
//            Runnable task = () -> {
//                Result result = seckillService.startSeckilDBPCC_TWO(killId, userId);
//                if (result != null) {
//                    log.info("秒杀成功，用户：{}{}", userId, result.get("msg"));
//                } else {
//                    log.info("秒杀失败，用户：{}{}", userId, SeckillStatEnum.MUCH.getInfo());
//                }
//                latch.countDown();
//            };
            Runnable task= new Runnable() {
                @Override
                public void run() {
                    Result result = seckillService.startSeckilDBPCC_TWO(killId, userId);
                if (result != null) {
                    log.info("秒杀成功，用户：{}{}", userId, result.get("msg"));
                } else {
                    log.info("秒杀失败，用户：{}{}", userId, SeckillStatEnum.MUCH.getInfo());
                }
                latch.countDown();
                }
            };
            executor.execute(task);
        }
        try {
            latch.await();//等待所有人的任务结束
            seckillCount = successKilledMapper.getSeckillCount(seckillId);
            log.info("悲观锁UPDATE锁表，一共秒杀出{}件商品", seckillCount);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return Result.ok(seckillCount);
    }
}
